package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.constants.message.ArticleConstants;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.utils.common.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    private ApArticleMapper apArticleMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private AdminFeign adminFeign;

    //计算热点文章
    @Override
    public void computeHotArticle() {
        //查询最近5天的article
        String date = LocalDateTime.now().minusDays(5).format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
        List<ApArticle> articleList = apArticleMapper.selectArticleByDate(date);
        //计算文章分值
        List<HotArticleVo> hotArticleVoScoreList = getHotArticleVoScore(articleList);
        //非空判断
        if (hotArticleVoScoreList.size() == 0) {
            CustException.cust(AppHttpCodeEnum.ADMING_CHANNEL_NOT_EXIST, "文章不存在,无法计算文章分值");
        }
        //每个频道缓存对应的30条热点文章
        cacheArticleRedis(hotArticleVoScoreList);
    }

    //重新计算文章热度,更新redis缓存
    @Override
    public void updateApArticle(AggBehaviorDTO aggBehavior) {
        //根据articleId查询数据库
        ApArticle apArticle = apArticleMapper.selectById(aggBehavior.getArticleId());
        //非空判断
        if (apArticle == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //数据库有数据,将Agg数据修改进去
        if (aggBehavior.getLike() != 0) {
            apArticle.setLikes(apArticle.getLikes() == null ? (int) aggBehavior.getLike() : (int) (apArticle.getLikes() + aggBehavior.getLike()));
        }
        if (aggBehavior.getCollect() != 0) {
            apArticle.setCollection(apArticle.getCollection() == null ? (int) aggBehavior.getCollect() : (int) (apArticle.getCollection() + aggBehavior.getCollect()));
        }
        if (aggBehavior.getComment() != 0) {
            apArticle.setComment(apArticle.getComment() == null ? (int) aggBehavior.getComment() : (int) (apArticle.getComment() + aggBehavior.getComment()));
        }
        if (aggBehavior.getView() != 0) {
            apArticle.setViews(apArticle.getViews() == null ? (int) aggBehavior.getView() : (int) (apArticle.getViews() + aggBehavior.getView()));
        }
        //重新计算分数
        int score = computeScore(apArticle);
        //判断是否是今天发布的文章
        String publishStr = DateUtils.dateToString(apArticle.getPublishTime());
        String nowStr = DateUtils.dateToString(new Date());
        if (publishStr.equals(nowStr)){
            score = score*3;
            //当天热点数据 *3
        }
        //将频道内容缓存到redis
        extracted(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        //将推荐列表内容缓存到redis
        extracted(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
    }

    //缓存内容到redis
    private void extracted(ApArticle apArticle, int score, String cacheKey) {
        //根据key查询redis
        String result = redisTemplate.opsForValue().get(cacheKey);
        //将查询到的JSON字符串解析成对象
        List<HotArticleVo> hotArticleVos = JSON.parseArray(result, HotArticleVo.class);
        //遍历集合
        boolean flag = false;
        for (HotArticleVo hotArticleVo : hotArticleVos) {
            //判断是否存在当前文章
            if (hotArticleVo.getId().equals(apArticle.getId())) {
                //有,修改分值
                hotArticleVo.setScore(score);
                //修改flag
                flag = true;
                //因为集合中每个文章不会重复,找到了就直接结束循环
                break;
            }
        }
        //判断flag
        if (!flag) {
            //热点文章集合没有当前文章,直接新增
            HotArticleVo hotArticleVo1 = new HotArticleVo();
            BeanUtils.copyProperties(apArticle, hotArticleVo1);
            hotArticleVo1.setScore(score);
            //添加到集合
            hotArticleVos.add(hotArticleVo1);
        }
        //将更新后的数据重新排序并截取30个推送到redis
        List<HotArticleVo> collect = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).limit(30).collect(Collectors.toList());
        //将新的集合推送到redis
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(collect));
    }

    //缓存文章到redis
    public void cacheArticleRedis(List<HotArticleVo> hotArticleVoScoreList) {
        //查询所有频道
        ResponseResult<List<AdChannel>> listResponseResult = adminFeign.selectChannels();
        //检查响应
        if (!listResponseResult.checkCode()) {
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR, "频道信息查询失败!");
        }
        //获取所有频道集合
        List<AdChannel> channelList = listResponseResult.getData();
        //遍历channelList集合和article集合,过滤出每个频道下的article
        for (AdChannel adChannel : channelList) {
            List<HotArticleVo> articleVosToChannelList = hotArticleVoScoreList.stream().filter(hotArticleVo -> {
                return hotArticleVo.getChannelId().equals(adChannel.getId());
            }).collect(Collectors.toList());
            //将对应频道的articleVo集合存入redis
            saveToRedis(articleVosToChannelList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + adChannel.getId());
        }
        //将推荐页的articleVo集合存入redis
        saveToRedis(hotArticleVoScoreList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
    }

    //将对应频道的articleVo集合存入redis
    public void saveToRedis(List<HotArticleVo> articleVosToChannelList, String cacheKey) {
        //遍历HotArticleVo集合,排序后截取30条数据
        List<HotArticleVo> newList = articleVosToChannelList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).limit(30).collect(Collectors.toList());
        //存入redis
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(newList));
    }

    //获取热点文章分数
    private List<HotArticleVo> getHotArticleVoScore(List<ApArticle> articleList) {
        //遍历article集合,转换成articleVo,并计算分值
        List<HotArticleVo> hotArticleVoList = articleList.stream().map(article -> {
            //创建hotArticleVo
            HotArticleVo hotArticleVo = new HotArticleVo();
            //将article属性复制到hotArticleVo并计算分值
            BeanUtils.copyProperties(article, hotArticleVo);
            int score = computeScore(article);
            hotArticleVo.setScore(score);
            return hotArticleVo;
        }).collect(Collectors.toList());
        return hotArticleVoList;
    }

    //计算文章分值
    private int computeScore(ApArticle article) {
        //计算热点文章分值
        //阅读:1
        int score = 0;
        if (article.getViews() != null) {
            score += article.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        //点赞:3
        if (article.getLikes() != null) {
            score += article.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        //评论:5
        if (article.getComment() != null) {
            score += article.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        //:收藏:8
        if (article.getCollection() != null) {
            score += article.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}
